package com.kingyea.mobilepolice.apimanage.accesscontrol.handle;

import com.google.common.collect.Maps;
import com.google.common.util.concurrent.AtomicLongMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Map;
import java.util.TimerTask;
import java.util.prefs.Preferences;

/**
 * @author chenjw
 * @version v1.0.0
 * @description
 * @date Created in 2018/4/4 10:16
 */

public class InvocationCount {
    private static final Logger LOGGER = LoggerFactory.getLogger(InvocationCount.class);

    private final static String KEY_CURRENT_MONTH_COUNT = "MonthCount";

    private final static String KEY_CURRENT_WEEK_COUNT = "WeekCount";

    private final static String KEY_CURRENT_DAILY_COUNT = "DailyCount";

    private final static String KEY_CURRENT_HOUR_COUNT = "HourCount";

    public static AtomicLongMap<String> atomicMap = AtomicLongMap.create();
    /**
     * 复位计数器定时器
     **/
    java.util.Timer timer = new java.util.Timer(true);
    /**
     * 月调用次数计数器
     **/
    private RecordableCounter monthCounter;
    /**
     * 周调用次数计数器
     **/
    private RecordableCounter weekCounter;
    /**
     * 日调用次数计数器
     **/
    private RecordableCounter dailyCounter;
    /**
     * 小时调用次数计数器
     **/
    private RecordableCounter hourCounter;
    /**
     * 每个月调用次数最大值
     **/
    private long monthCount = Long.MAX_VALUE;
    private long weekCount = Long.MAX_VALUE;
    private long dailyCount = Long.MAX_VALUE;
    private long hourCount = Long.MAX_VALUE;
    private String partialPath;
    private boolean inited = false;
    private String pathName;

    public static String getKeyCurrentMonthCount() {
        return KEY_CURRENT_MONTH_COUNT;
    }

    public static String getKeyCurrentWeekCount() {
        return KEY_CURRENT_WEEK_COUNT;
    }

    public static String getKeyCurrentDailyCount() {
        return KEY_CURRENT_DAILY_COUNT;
    }

    public static String getKeyCurrentHourCount() {
        return KEY_CURRENT_HOUR_COUNT;
    }

    /**
     * 启动定时器定时复位计数器
     **/
    public void schedule() {
        this.timer.scheduleAtFixedRate(new TimerTask() {

            @Override
            public void run() {
                final GregorianCalendar calendar = new GregorianCalendar();
                final int minute = calendar.get(Calendar.MINUTE);
                if (minute == 0) {
                    InvocationCount.this.hourCounter.reset(InvocationCount.this.getPathName());
                }
                final int hourOfDay = calendar.get(Calendar.HOUR_OF_DAY);
                if (hourOfDay == 0) {
                    InvocationCount.this.dailyCounter.reset(InvocationCount.this.getPathName());
                }
                final int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
                if (dayOfWeek == 2) {
                    InvocationCount.this.weekCounter.reset(InvocationCount.this.getPathName());
                }
                final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
                if (dayOfMonth == 1) {
                    InvocationCount.this.monthCounter.reset(InvocationCount.this.getPathName());
                }
            }
        }, 0, 58 * 1000);// 58秒执行一次，定时清零计数器
    }

    /**
     * InvocationCountCondition创建后必须进行初始化
     **/
    public void init(final String partialPath) {
        this.setPartialPath(partialPath);
        final Preferences preferences = this.getPreferences();
        this.hourCounter = new RecordableCounter(preferences, KEY_CURRENT_HOUR_COUNT, this.getPathName());
        this.dailyCounter = new RecordableCounter(preferences, KEY_CURRENT_DAILY_COUNT, this.getPathName());
        this.weekCounter = new RecordableCounter(preferences, KEY_CURRENT_WEEK_COUNT, this.getPathName());
        this.monthCounter = new RecordableCounter(preferences, KEY_CURRENT_MONTH_COUNT, this.getPathName());
        this.schedule();
        this.setInited(true);
    }

    public void resets() {
        this.reset("special");
        this.reset("whole");
    }

    public void reset(final String type) {
        // com.codahale.metrics.InvocationCount.ALL
        final Preferences preferences = Preferences.userRoot()
                .node("com.codahale.metrics.InvocationCount.C00-00000035_S10-00000049_" + type);// special//whole
        final RecordableCounter hourCounter = new RecordableCounter(preferences, KEY_CURRENT_HOUR_COUNT,
                this.getPathName());
        final RecordableCounter dailyCounter = new RecordableCounter(preferences, KEY_CURRENT_DAILY_COUNT,
                this.getPathName());
        final RecordableCounter weekCounter = new RecordableCounter(preferences, KEY_CURRENT_WEEK_COUNT,
                this.getPathName());
        final RecordableCounter monthCounter = new RecordableCounter(preferences, KEY_CURRENT_MONTH_COUNT,
                this.getPathName());
        final GregorianCalendar calendar = new GregorianCalendar();
        calendar.get(Calendar.MINUTE);
        hourCounter.reset(this.getPathName());
        calendar.get(Calendar.HOUR_OF_DAY);
        dailyCounter.reset(this.getPathName());
        calendar.get(Calendar.DAY_OF_WEEK);
        weekCounter.reset(this.getPathName());
        calendar.get(Calendar.DAY_OF_MONTH);
        monthCounter.reset(this.getPathName());
    }

    /**
     * 如果没有禁止调用则会调用些方法
     */
    public boolean heartBeat() {
        // System.out.println(this.getPathName());
        if (atomicMap.get(this.getPathName() + KEY_CURRENT_MONTH_COUNT) < this.monthCount) {
            this.monthCounter.inc(this.getPathName());
        } else {
            return false;
        }
        if (atomicMap.get(this.getPathName() + KEY_CURRENT_WEEK_COUNT) < this.weekCount) {
            this.weekCounter.inc(this.getPathName());
        } else {
            return false;
        }
        if (atomicMap.get(this.getPathName() + KEY_CURRENT_DAILY_COUNT) < this.dailyCount) {
            this.dailyCounter.inc(this.getPathName());
        } else {
            return false;
        }
        if (atomicMap.get(this.getPathName() + KEY_CURRENT_HOUR_COUNT) < this.hourCount) {
            this.hourCounter.inc(this.getPathName());
        } else {
            return false;
        }
        return true;
    }

    /**
     * 检查是否已经初始化
     *
     * @return
     */
    public boolean checkInited() {
        if (!this.isInited()) {
            return false;
        }
        return true;
    }

    public Map<String, Object> resultEntries() {
        final Map<String, Object> entries = Maps.newHashMap();
        if (atomicMap.get(this.getPathName() + KEY_CURRENT_MONTH_COUNT) >= this.monthCount) {
            entries.put(Keys.MESSAGE, "一个月内调用次数超出限制");
            entries.put(Keys.MAX_COUNT, this.monthCount);
            entries.put(Keys.PERIOD, "月");
        } else if (atomicMap.get(this.getPathName() + KEY_CURRENT_WEEK_COUNT) >= this.weekCount) {
            entries.put(Keys.MESSAGE, "一周内调用次数超出限制");
            entries.put(Keys.MAX_COUNT, this.weekCount);
            entries.put(Keys.PERIOD, "周");
        } else if (atomicMap.get(this.getPathName() + KEY_CURRENT_DAILY_COUNT) >= this.dailyCount) {
            entries.put(Keys.MESSAGE, "一天内调用次数超出限制");
            entries.put(Keys.MAX_COUNT, this.dailyCount);
            entries.put(Keys.PERIOD, "天");
        } else if (atomicMap.get(this.getPathName() + KEY_CURRENT_HOUR_COUNT) >= this.hourCount) {
            entries.put(Keys.MESSAGE, "一小时内调用次数超出限制");
            entries.put(Keys.MAX_COUNT, this.hourCount);
            entries.put(Keys.PERIOD, "小时");
        }
        return entries;
    }

    /**
     * 判断此次请求是否要禁止调用
     **/
    public boolean forbidden() {
        System.out.println(this.getPathName() + ":" + atomicMap.get(this.getPathName() + KEY_CURRENT_DAILY_COUNT));
        /*
         * return (this.monthCounter.getCount() >= this.monthCount) || (this.weekCounter.getCount() >= this.weekCount)
         * || (this.dailyCounter.getCount() >= this.dailyCount) || (this.hourCounter.getCount() >= this.hourCount);
         */
        return (atomicMap.get(this.getPathName() + KEY_CURRENT_MONTH_COUNT) >= this.monthCount)
                || (atomicMap.get(this.getPathName() + KEY_CURRENT_WEEK_COUNT) >= this.weekCount)
                || (atomicMap.get(this.getPathName() + KEY_CURRENT_DAILY_COUNT) >= this.dailyCount)
                || (atomicMap.get(this.getPathName() + KEY_CURRENT_HOUR_COUNT) >= this.hourCount);
    }

    private Preferences getPreferences() {
        final String pathName = this.buildPathName();

        if (LOGGER.isDebugEnabled()) {
            LOGGER.info("path name:" + pathName);
        }
        this.setPathName(pathName);
        return Preferences.userRoot().node(pathName);
    }

    private String buildPathName() {
        if ((this.partialPath == null) || this.partialPath.isEmpty()) {
            throw new RuntimeException("partialPath不能为空");
        }
        //return this.getClass().getName() + "." + this.partialPath;
        return "." + this.partialPath;
    }

    public RecordableCounter getMonthCounter() {
        return this.monthCounter;
    }

    public void setMonthCounter(final RecordableCounter monthCounter) {
        this.monthCounter = monthCounter;
    }

    public RecordableCounter getWeekCounter() {
        return this.weekCounter;
    }

    public void setWeekCounter(final RecordableCounter weekCounter) {
        this.weekCounter = weekCounter;
    }

    public RecordableCounter getDailyCounter() {
        return this.dailyCounter;
    }

    public void setDailyCounter(final RecordableCounter dailyCounter) {
        this.dailyCounter = dailyCounter;
    }

    public RecordableCounter getHourCounter() {
        return this.hourCounter;
    }

    public void setHourCounter(final RecordableCounter hourCounter) {
        this.hourCounter = hourCounter;
    }

    public long getMonthCount() {
        return this.monthCount;
    }

    public void setMonthCount(final long monthCount) {
        this.monthCount = monthCount;
    }

    public long getWeekCount() {
        return this.weekCount;
    }

    public void setWeekCount(final long weekCount) {
        this.weekCount = weekCount;
    }

    public long getDailyCount() {
        return this.dailyCount;
    }

    public void setDailyCount(final long dailyCount) {
        this.dailyCount = dailyCount;
    }

    public long getHourCount() {
        return this.hourCount;
    }

    public void setHourCount(final long hourCount) {
        this.hourCount = hourCount;
    }

    public String getPartialPath() {
        return this.partialPath;
    }

    public void setPartialPath(final String partialPath) {
        this.partialPath = partialPath;
    }

    public boolean isInited() {
        return this.inited;
    }

    public void setInited(final boolean inited) {
        this.inited = inited;
    }

    public java.util.Timer getTimer() {
        return this.timer;
    }

    public void setTimer(final java.util.Timer timer) {
        this.timer = timer;
    }

    public String getPathName() {
        return this.pathName;
    }

    public void setPathName(final String pathName) {
        this.pathName = pathName;
    }

}
